Spring cloud Gateway 路由谓词工厂


Gateway 是 Spring cloud 官方所推出的第二代网关(API Gateway)框架,在为服务系统中,网关的主要作用就是当用户调用的所有微服务,都要经过网关。

在为服务中,除于不同微服务可能会带来不同的开发语言和协议,因此需要通过网关来处理服务的调用

网关统一向外部系统提供 REST API,在 Spring cloud 中,使用 Zuul、Gateway 等作为网关可以实现出动态路由、监控、回退、安全功能等。

Spring cloud Gateway


Gateway 是 Spring cloud 官方所推出的第二代网关(API Gateway)框架,在为服务系统中,网关的主要作用就是当用户调用的所有微服务,都要经过网关。

在为服务中,除于不同微服务可能会带来不同的开发语言和协议,因此需要通过网关来处理服务的调用

网关统一向外部系统提供 REST API,在 Spring cloud 中,使用 Zuul、Gateway 等作为网关可以实现出动态路由、监控、回退、安全功能等。

特点

Gateway 基于 Spring Framework 5、Project Reactor 、Spring boot 2.0 构建,可以匹配任何请求属性的路由,能编写谓词(Predicates)以及过滤器(Filters)等。

谓词主要是用于确定给定输入的 true 或 false,在网关中谓词和过滤器也可以用于特定的路由。

过滤器可以重写数据,但由于 Gateway 是异步的,如果需要对响应的 body 进行修改需要使用 writeWith() 所提供的 GlobalFilter、GatewayFilter 类型。

其中 GlobalFilter 是对所有路由有效的,而 GatewayFilter 类型仅对指定范围生效

除此之外 Gateway 还支持路径重写、动态路由和集成了 Hystrix 断路器以及 DiscoveryClient 的集成和限流。

工作流程

首先 HTTP/HTTPS 强求 Spring cloud Gateway,DispatcherHandler 接受请求,并通过 RoutePredicateHandlerMapping 进行路由匹配,如果网关路由与请求的路由进行匹配 则将请求发送到 FilteringWebHandler,如果不匹配,那么则将请求发送给 DispatcherHandler 进行处理。

最后 FilteringWebHandler 通过自定义的过滤器来发送请求,这会将请求转发到具体的服务中,最后处理结果给用户。

谓词接口和谓词工厂

谓词(Predicate),在 Java 8 中引入的一个函数式接口,主要用于接受输入参数并返回布尔值的结果,Spring cloud Gateway 通过 Predicate 接口来判断当前路由是否满足指定条件。

谓词工厂(Route Predicate Factories)主要作用就是当符合谓词条件就使用该路由进行匹配,否则就忽略。

Spring cloud Gateway 的路由规则是由 RouteDefinitionLocator 类进行管理,默认情况下使用 Spring Boot 的 @ConfigurationProperties 机制来加载属性。

实现

Java API


可以通过最简单的 Java API 的方式来构建路由,主要通过 @Bean 来实现一个自定义的 RouteLocator 类来实现 自定义路由转发规则。在此之前,需要通过添加 gateway 依赖来完成:

1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

由于 Spring cloud gateway 使用的是 Netty+WebFlux 实现的,因此不需要引入 Web 模块依赖。

之后在启动类添加自定义 RouteLocator 类即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;

/**
* 将请求转发给 baidu.com
*
* @author kunlun
* @date 2021/7/8
*/
@SpringBootApplication
public class DemoApplication {

public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}

@Bean
public RouteLocator routeLocator (RouteLocatorBuilder builder) {
return builder.routes().
route("guonei", r -> r.path("/guonei/**")
.uri("http://news.baidu.com/")).build();
}

}

在上述的过程中,route 主要将与 guonei 路由想匹配的通过 Spring cloud Gateway 转发到了 http://news.baidu.com/guonei 中,因此访问 http://localhost:8081/guonei 就是访问 http://news.baidu.com/guonei

其中 r.path 参数下的 /guonei/** 指的是这将会匹配包含所有任何后缀携带 /guonei 的路径模式,还需要注意的是 Spring cloud Gateway 支持两种的配置,上面所实现的是基于RouteLocator 的实现,还有 properties 配置文件的实现

application.yml / application.proerties

1
2
3
4
5
6
7
8
9
10
11
12
13
spring:
cloud:
gateway:
routes:
- id: guonei
uri: http://news.baidu.com/
predicates:
- Path=/guonei
application:
name: GatewayForward

server:
port: 8081

与 Java API 一样,访问 http://localhost:8081/guonei 就是访问 http://news.baidu.com/guonei,同样的实现了路由的功能。

路由规则(RoutePredicateFactory)

在 Gateway 中,主要通过 RoutePredicateFactory(谓词工厂)来创建谓词(Predicate),因此也提供了很多种谓词工厂,这也是在上面所提到的:“谓词工厂(Route Predicate Factories)主要作用就是当符合谓词条件就使用该路由进行匹配,否则就忽略。”

通过谓词工厂,使得开发人员可以简单配置即可获得很多需要和想要的路由规则,及谓词(Predicate),这些路由规则会根据 HTTP 请求进行不同属性来匹配,其中最为主要的为下述几类:

RoutePredicateFactory type info
AfterRoute……(省略 PredicateFactory) datetime 请求时间满足在配置时间之后
BeforeRoute datetime 请求时间满足在配置时间之前
BetweenRoute datetime 请求时间满足在配置时间之前
CookieRoute Cookie 请求指定了 Cookie 正则匹配的指定值
HeaderRoute Header(数据头) 请求指定了请求上下文所匹配的指定值
CloudFoundryRouteServiceRoute Header 请求 Headers 是否包含了指定的名称
MethodRoute Method(方法) 请求的方法是否匹配配置的方法
PathRoute Path(路径) 请求路径是否匹配指定值
Query Queryparam(查询参数) 请求查询的查询参数与配置文件的相匹配
RemoteAddreRoute Remoteaddr(远程地址) 远程地址是否匹配指定值
HostRoute Host(主机) 请求主机是否匹配指定值

After

AfterRoutePredicateFactory(After路由谓词工厂)是一个 datetime(日期时间) 类型的为此,他主要表达的是如果请求在配置文件中 时间在配置文件之后 发生的请求允许。

1
2
3
4
5
6
7
8
9
10
11
12
13
spring:
cloud:
gateway:
routes:
- id: after
uri: http://news.baidu.com/
predicates:
- After=2022-01-20T18:06:06+08:00[Asia/Shanghai]
application:
name: GatewayForward

server:
port: 8081

After 的核心意思就是只能在配置规定后的时间进行访问,但只能通过 UTC + 时区的方式来添加规则,否则将直接报错。After 的主要作用就是在 2021 年 1 月 20 日之后才可进行访问,否则将会直接报错,直接访问 http://localhost:8081/ 即可。

Before

Before 也是属于 datetime 类型之一,其核心含义是当请求满足配置时间之前,而之前的 After 路由谓词工厂只是满足配置时间之后:

1
2
3
4
5
6
7
8
9
10
11
12
13
spring:
cloud:
gateway:
routes:
- id: after
uri: http://news.baidu.com/
predicates:
- Before=2022-01-20T18:06:06+08:00[Asia/Shanghai]
application:
name: GatewayForward

server:
port: 8081

Between

Between 可以理解为是两个 UTC 时间之间的请求,因此他有两个参数,分别是 datetime1 以及 datetime2 这是一个 ZonedDateTime 对线,因此请求需要满足 datetione1 之后且 datetime2 之前的请求:

1
2
3
4
5
6
7
8
9
10
11
spring:
cloud:
gateway:
routes:
- id: after
uri: http://news.baidu.com/
predicates:
- Between=2019-01-20T18:06:06+08:00[Asia/Shanghai],2022-01-20T18:06:06+08:00[Asia/Shanghai]
application:
name: GatewayForward

如上面的 Between 路由规则是,在 2019-01-20T18:06:06+08:00[Asia/Shanghai] 之后,2022-01-20T18:06:06+08:00[Asia/Shanghai] 之前的请求将会进行匹配,否则将会忽视。

CookieRoutePredicateFactory 主要用于匹配指定值,虽然很多作者说直接可以一条参数走到低:mycookie,mycookievalue,但很遗憾并没有作用,因此只能通过全展开的方式来实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
spring:
cloud:
gateway:
routes:
- id: after
uri: http://news.baidu.com/
predicates:
- name: Cookie
args:
name: meteor_login_token
regexp: ipQdtc89kEO7-JaBWLdgwPHzX9oDy6l-2qXmZ_OeHye
application:
name: GatewayForward

server:
port: 8081

在上述的 cookie 中,主要指定了 cookie 名字 meteor_login_token,以及 cookie_value ipQdtc89kEO7-JaBWLdgwPHzX9oDy6l-2qXmZ_OeHye,因此如果请求的 cookie 与配置文件相匹配则通过,否则将会忽略。

Header 即请求上下文的头部信息,当请求与配置的 Header 一致时进行转发,在官方文档中被称之为:X-Request-Id 与正则表达式合在一起进行匹配,但并没有什么作用。因此我们可以理解为 Header 请求头是根据上下文头部信息来指定服务器的域名和服务器正在侦听的 TCP 端口号:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
spring:
cloud:
gateway:
routes:
- id: after
uri: http://news.baidu.com/
predicates:
- Header=Host,localhost:8081

application:
name: GatewayForward

server:
port: 8081

当请求的上下文头部信息与配置文件相互匹配则允许请求,否则将会直接进行忽略。

Host

Host 路由谓词工厂与 Header 类似,或者说两者都可以实现其效果,但 Host 路由谓词工厂很明显比 Hedaer 更加的简单且好理解。因此他主要的作用就是匹配所请求的主机是否和配置文件相互一致,否则将不具备转发功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
spring:
cloud:
gateway:
routes:
- id: after
uri: http://news.baidu.com/
predicates:
- Host=localhost:8081

application:
name: GatewayForward

server:
port: 8081

在上述配置文件中,请求的主机是 localhost:8081 因此与配置文件的信息相互匹配,可以通过网关使用路由进行跳转。

Method


Method 路由谓词工厂相比上述几个 header 类型的谓词工厂显得特别实用,他主要用指定访问资源以响应预检请求时允许的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
spring:
cloud:
gateway:
routes:
- id: after
uri: http://news.baidu.com/
predicates:
- Method=GET,POST

application:
name: GatewayForward

server:
port: 8081

就比如上述的配置文件,我们允许了 GET,POST 方法请求,但不允许 PUT 方法请求,因此使用该方法请求将会直接被网关所进行拦截。

Query


Query 路由谓词工厂主要是当 请求查询的查询参数与配置文件的相匹配,就比如 localhost:8081?name=111 正好与配置文件中的 query=namename 相互匹配,则允许请求:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
spring:
cloud:
gateway:
routes:
- id: after
uri: http://news.baidu.com/
predicates:
- Query=name

application:
name: GatewayForward

server:
port: 8081

RemoteAddre

RemoteAddre 与 Header 路由谓词工厂类似,简单理解就是他们的进阶版,他可以根据 CIDR 表示法进行匹配,也就是说当你写入规则是 192.168.0.1/24 时,你 192.168.0.104/24 也可以请求此接口,但如果你不是这个网段下的,则会被拒绝

1
2
3
4
5
6
7
8
9
10
11
12
13
14
spring:
cloud:
gateway:
routes:
- id: remoteaddr_route
uri: http://news.baidu.com
predicates:
- RemoteAddr=192.168.0.1/24

application:
name: GatewayForward

server:
port: 8081

localhost 是 127.0.0.1 的地址映射,因此在 RemoteAddr 路由谓词工厂规则中他会被直接拒绝。

Weight


Weight 是权重路由谓词工厂,当 Gateway 中有多个路由网关配置时可以发挥作用,他主要分为 组(group) 以及 权重(weight) 进行组合:

1
2
3
4
5
6
7
8
9
10
11
12
spring:
cloud:
gateway:
routes:
- id: weight_one
uri: http://news.baidu.com
predicates:
- Weight=group1, 1
- id: weight_two
uri: http://baidu.com
predicates:
- Weight=group1, 8

就比如上述的配置中,weight_two 的权重比 weight_one 高,因此网关匹配的是 weight_two 的路由。

Gateway consul 服务转发


在正常的微服务架构中,都会选择依赖服务中心来进行注册,这通常需要对每个服务提供者进行单独的配置,Gateway 的出现爱你提供了默认转发的工作,当网关注册到服务中心后,网关则会代替服务中心来提供转发的服务

1
2
3
4
5
6
7
8
9
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

application.yml

application.yml 配置文件中,需要开启服务转发的选项,即 spring.cloud.gateway.discovery.locator.enabled 即可,之后在启动类中添加 @EnableDiscoveryClient 注解以此来实现服务发现:

1
2
3
4
5
6
7
8
9
10
11
12
13
spring:
application:
name: Gateway Consul
cloud:
consul:
host: localhost
port: 8500
gateway:
discovery:
locator:
enabled: true
server:
port: 8515

本文使用《江雪分析公开知识存储库知识共享许可证》进行发布